/*
 *  Copyright 2010 Alexey Frishman (alexey.frishman@gmail.com)
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not
 *  use this file except in compliance with the License. You may obtain a copy of
 *  the License at
 *  
 *  http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  License for the specific language governing permissions and limitations under
 *  the License.
 * 
 */

package org.json4g.serialization;

import static org.json4g.TypeUtil.getClassType;

import org.json4g.serialization.policy.SerializationPolicy;

import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.gwtent.reflection.client.ClassType;
import com.gwtent.reflection.client.Field;
import com.gwtent.reflection.client.Type;

/**
 * Serializer for Java Beans. Which fields to serialize and other rules are defined in the SerializationPolicy
 */
public class JsonBeanSerializer extends AbstractJsonSerializer {

    private JsonSerializer fieldSerializer;
    private SerializationPolicy policy;

    /**
     * @param fieldSerializer
     *            if the bean serializer decides that field should be serialized, it delegates then the actual
     *            serialization/deserialization of the field to the fieldSerializer
     * @param policy
     *            rules of serialization
     */
    public JsonBeanSerializer(JsonSerializer fieldSerializer, SerializationPolicy policy) {
        this.fieldSerializer = fieldSerializer;
        this.policy = policy;
    }

    @Override
    public Object deserialize(JSONValue json, Field parentField) {
        assertSupported(json, parentField.getType());
        ClassType classType = parentField.getType().isClass();
        JSONObject jsonObject = json.isObject();
        Object instance = classType.findConstructor().newInstance();
        for (String name : jsonObject.keySet()) {
            Field childField = policy.getFieldNamePolicy().getFieldByName(classType, name);
            if (policy.getFieldCondition().isSatisfiedForDeserialization(childField)) {
                JSONValue jsonValue = jsonObject.get(name);
                Object value = fieldSerializer.deserialize(jsonValue, childField);
                childField.setFieldValue(instance, value);
            }
        }
        return instance;
    }

    @Override
    public JSONValue serialize(Object object) {
        assertSupported(object);
        Field[] fields = getClassType(object).getFields();
        JSONObject jsonObject = new JSONObject();
        for (Field field : fields) {
            Object fieldValue = field.getFieldValue(object);
            if (policy.getFieldCondition().isSatisfiedForSerialization(object, field)) {
                jsonObject.put(policy.getFieldNamePolicy().getNameForField(field), fieldSerializer.serialize(fieldValue));
            }
        }
        return jsonObject;
    }

    @Override
    protected boolean supports(Type type) {
        return type != null;
    }

    @Override
    protected boolean supports(JSONValue json) {
        return super.supports(json) && json.isObject() != null;
    }

}
